#include <jni.h>
#include <string>

#include <rtmp.h>
#include "defind-util.h"
#include "SVideoChannel.h"
#include "SAudioChannel.h"
#include <pthread.h>
#include "safe_queue.h"

namespace {
    SVideoChannel *videoChannel = nullptr;
    SAudioChannel *audioChannel = nullptr;
    pthread_t pthread;

    // 是否播放 防止重复点击
    bool isStart;

    // 是否可以推流
    bool isPush;

    // 生产者消费者队列 【不区分音频，视频】
    SafeQueue<RTMPPacket *> packets;

    // 记录时间戳
    uint32_t start_time = 0;
}

void releaseRTMPPacket(RTMPPacket **packet) {
    if (packet) {

        RTMPPacket_Free(*packet);
//        delete packet;
        *packet = nullptr;
    }
}

void callback(RTMPPacket *packet) {
//    LOG_D("szj callback %d",start_time)
    if (packet) {
//        if (packet->m_packetType == RTMP_PACKET_TYPE_AUDIO) {
//            LOG_D("insert type: 音频包")
//        } else if (packet->m_packetType == RTMP_PACKET_TYPE_VIDEO) {
//            LOG_D("insert type: 视频包")
//        }
        if (packet->m_nTimeStamp == -1) {
            // 如果是sps + pps 没有时间戳，
            // 如果是I帧有时间戳
            packet->m_nTimeStamp = RTMP_GetTime() - start_time;
        }
        packets.insert(packet);
    }
}

// TODO 初始化工作
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_SPush_native_1init(JNIEnv *env, jobject thiz) {
    videoChannel = new SVideoChannel;

    audioChannel = new SAudioChannel;

//  音视频回调监听
    videoChannel->setVideoCallBack(callback);
    audioChannel->setAudioCallback(callback);

    // 队列释放工作
    packets.setReleaseCallBack(releaseRTMPPacket);

}

void *start_play_(void *vP) {
    char *path = static_cast<char *>(vP);
    LOG_D("child path=%s", path)
    RTMP *rtmp;
    int result = FALSE;
    do {
        // TODO step1 申请内存
        rtmp = RTMP_Alloc();
        if (rtmp == nullptr) {
            break;
        }

        // TODO step2 初始化rtmp
        RTMP_Init(rtmp);
        rtmp->Link.timeout = 5;// 设置链接超时时间

        // TODO step3 设置地址
        result = RTMP_SetupURL(rtmp, path);
        if (result == FALSE) {
            LOG_D("地址设置失败")
            break;
        }

        // TODO step4 设置输出模式
        RTMP_EnableWrite(rtmp);

        // TODO step5 链接服务器
        result = RTMP_Connect(rtmp, nullptr);
        if (result == FALSE) {
            LOG_D("服务器链接失败")
            break;
        }

        // 记录时间戳
        start_time = RTMP_GetTime();


        // TODO step6 链接流
        result = RTMP_ConnectStream(rtmp, 0);
        if (result == FALSE) {
            LOG_D("链接流失败")
            break;
        }

        // 此时服务器链接成功，可以向服务器推流了
        isPush = true;
        LOG_D("服务器链接成功，可以发包了")

        // 队列开始工作
        packets.setWork(true);
        // 从队列中获取包，直接发送给服务器
        RTMPPacket *rtmpPacket = nullptr;

        // TODO 当可以发包的释放，发送音频
        if (audioChannel) {
//            audioChannel->getSequenceHeader();
        }

        while (isPush) {
            packets.get(rtmpPacket);

//            LOG_D("消费packet1")
            // 因为当前是队列是阻塞式的，所以这里需要break
            if (!isPush) {
                break;
            }

            if (!rtmpPacket) {
                continue;
            }

            rtmpPacket->m_nInfoField2 = rtmp->m_stream_id;
            // 发包
            result = RTMP_SendPacket(rtmp, rtmpPacket, 1);

            // 释放 RTMPPacket
            releaseRTMPPacket(&rtmpPacket);
            if (!result) {
                LOG_D("发送失败") // 如果发送包失败，就会自动断开服务器
                break;
            }

        }

        releaseRTMPPacket(&rtmpPacket);
        isStart = false;
        isPush = false;
        packets.clear();

    } while (false);

    if (rtmp != nullptr) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
    }

    return nullptr;
}

// TODO 开始播放
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_SPush_native_1startPlay(JNIEnv *env, jobject thiz, jstring path) {
    char *RTMP_path = const_cast<char *>(env->GetStringUTFChars(path, nullptr));

    LOG_D("播放地址 = %s", RTMP_path);

    // 开启子线程，链接rtmp服务器
    pthread_create(&pthread, nullptr, start_play_, (void *) RTMP_path);

}


// TODO 停止播放
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_SPush_native_1stopPlay(JNIEnv *env, jobject thiz) {
}

// TODO 释放资源
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_SPush_native_1release(JNIEnv *env, jobject thiz) {
}

// TODO 视频NV21数据
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_VideoChannel_native_1video_1data(JNIEnv *env, jobject thiz,
                                                             jbyteArray data) {

    // 如果没有channel 或者 不可以推流，直接返回
    if (videoChannel == nullptr || !isPush) {
        return;
    }

    // jni -> C++
    jbyte *datas = env->GetByteArrayElements(data, nullptr);

    // 编码
    videoChannel->encodeData(datas);

    // 释放
    env->ReleaseByteArrayElements(data, datas, 0);


}

// TODO 视频宽高发生变化回调 初始化x264
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_VideoChannel_native_1video_1changed(JNIEnv *env,
                                                                jobject thiz,
                                                                jint width,
                                                                jint height,
                                                                jint fps,
                                                                jint bitrate) {
    LOG_D("width=%d\theight=%d\tfps=%d\tbitrate:%d", width, height, fps, bitrate)
    if (videoChannel != nullptr)
        videoChannel->initX264Encode(width, height, fps, bitrate);

}

// TODO 初始化faac编码器
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_AudioChannel_native_1initFaacEncoder(JNIEnv *env, jobject thiz,
                                                                 jint channel, jint sample_rate) {

    if (audioChannel) {
        LOG_D("初始化音视频Faac编码器")
        audioChannel->initFaacEncoder(channel, sample_rate);
    }
}

// TODO 获取样本数 【初始化faac成功后可以获取到】
extern "C"
JNIEXPORT jint JNICALL
Java_com_szj_spushrtmp_push_AudioChannel_getInputSample(JNIEnv *env, jobject thiz) {
    if (audioChannel) {
        return audioChannel->getInputSample();
    }
    return 0;
}

// TODO 音频数据 (通过faac编码器编码)
extern "C"
JNIEXPORT void JNICALL
Java_com_szj_spushrtmp_push_AudioChannel_native_1AudioData(JNIEnv *env, jobject thiz,
                                                           jbyteArray data_, jint len) {
    if (audioChannel) {
        jbyte *data = env->GetByteArrayElements(data_, nullptr);


        audioChannel->encodeData(data);

        // 释放byte
        env->ReleaseByteArrayElements(data_, data, 0);
    }
}